import {
  Voucher,
  VoucherGateway,
  putVoucherDto,
  postVoucherDto,
  disableVoucherDto,
  getVoucherByIdDto,
  VoucherReadInterfaceWithGrant,
  VoucherWriteInterfaceWithGrant,
  VoucherBookStatInterfaceWithGrant,
  VoucherMediaInterfaceWithGrant,
  Book,
  PutRes,
  PageData,
  BookQuery,
  PostMutiRes,
  getVoucherDto,
  WithPasterList,
  getByTradeIdDto,
  getVoucherBookStatDto,
  getVoucherAfterBookSortDto,
  DisableUUid32Res,
  patchVoucherAppBookDto,
  putVoucherMediaDto,
  deleteVoucherMediaDto,
  PostRes,
  DisableRes,
  RefVoucherMedia,
  RefVoucherMediaGateway,
  ENUM_VOUCHER,
} from "qqlx-core";

import { ViewBase, ViewBaseAction } from "../view-base";
import { VoucherSchema } from "@/river/voucher-core";
import { isStringNotValid, toNumber, toString } from "@lib/com.verify";

export class ViewVoucher
  extends ViewBase<Voucher>
  implements
    ViewBaseAction<Voucher>,
    VoucherReadInterfaceWithGrant<{}>,
    VoucherBookStatInterfaceWithGrant<{}>,
    VoucherWriteInterfaceWithGrant<{}>,
    VoucherMediaInterfaceWithGrant<{}>
{
  private static instance: ViewVoucher;
  static getInstance(options?: { isSingleton?: boolean }) {
    if (options?.isSingleton === false) {
      return new ViewVoucher();
    }

    if (!this.instance) this.instance = new ViewVoucher();
    return this.instance;
  }

  private constructor() {
    super();
    this.initial();
    this.insertDefaultCondition();
  }

  getSchema(): Voucher {
    return new VoucherSchema();
  }

  // data

  /** 从OCR结果中分析出财务记录 */
  getVoucherFromOcr(datas: { Name: string; Value: string }[], voucherType: ENUM_VOUCHER): Partial<Voucher> {
    if (!datas) throw { message: `无法识别此发票` };

    const _contractTime = this._getOcrDataStr(datas, "开票日期", true);
    const _saler = this._getOcrDataStr(datas, "销售方名称", true);
    const _buyer = this._getOcrDataStr(datas, "购买方名称", true);

    const _no = this._getOcrDataStr(datas, "发票号码", true);
    const _code = this._getOcrDataStr(datas, "发票代码");

    const _amount1 = this._getOcrDataStr(datas, "小写金额");
    const _amount2 = this._getOcrDataStr(datas, "价税合计(小写)");
    const _amount3 = this._getOcrDataStr(datas, "合计税额");

    const _typeName = this._getOcrDataStr(datas, "发票类型");
    const _creator = this._getOcrDataStr(datas, "开票人");
    const _remark = this._getOcrDataStr(datas, "备注");

    return {
      contractTime: new Date(`${_contractTime.replace(/年|月|日/g, "/")} 08:00`).getTime().toString(),
      contractSide: voucherType === ENUM_VOUCHER.INCOME_FAPIAO ? _saler : _buyer,
      contractSideOp: voucherType === ENUM_VOUCHER.INCOME_FAPIAO ? _buyer : _saler,
      code: [_no, _code].filter((e) => !!e).join("-"),
      amount: toNumber((_amount2 || _amount1).replace(/￥|¥/g, ""), 0),
      amountTax: toNumber(_amount3.replace(/￥|¥/g, ""), 0),
      remark: [_typeName, _remark].join("；"),
      type: voucherType,
    };
  }

  /** @strick 无法识别到的时候，会报错 */
  private _getOcrDataStr(datas: { Name: string; Value: string }[], name: string, strick = false) {
    const value = toString(datas?.find((e) => e.Name === name)?.Value);
    if (isStringNotValid(value)) {
      if (strick) throw { message: `无法识别${name}` };
    }
    return value;
  }

  // read

  async get(input: Omit<getVoucherDto, "page" | "conditionList">) {
    this.pending();
    const path = VoucherGateway.path;
    const dto = { page: this.data.entityListPage, conditionList: this.data.conditionList, ...input };
    const { list, total } = await this.$request.getByPut<getVoucherDto, PageData<Voucher & WithPasterList>>(path, dto);
    this.data.entityListTotal = total;
    return { list, total };
  }

  async getById(dto: getVoucherByIdDto) {
    this.pending();
    const path = `${VoucherGateway.path}/detail`;
    return this.$request.getByPut<getVoucherByIdDto, { first: Voucher; comboBooks: Book[] }>(path, dto);
  }

  getByTradeId(dto: getByTradeIdDto) {
    this.pending();
    const path = `${VoucherGateway.path}/tradeId`;
    return this.$request.getByPut<getByTradeIdDto, PageData<Voucher & WithPasterList>>(path, dto);
  }

  async getVoucherAfterBookSort(input: Omit<getVoucherAfterBookSortDto, "page" | "conditionList">) {
    this.pending();
    const dto = { page: this.data.entityListPage, conditionList: this.data.conditionList, ...input };

    const path = `${VoucherGateway.path}/book/sort`;
    const res = await this.$request.getByPut<getVoucherAfterBookSortDto, PageData<Voucher & WithPasterList>>(path, dto);
    this.data.entityListTotal = res.total;
    return res;
  }

  // stat

  getVoucherStatBook(dto: getVoucherBookStatDto) {
    const path = `${VoucherGateway.path}/book/stat`;
    return this.$request.getByPut<getVoucherBookStatDto, (BookQuery & { total: number })[]>(path, dto);
  }

  // write

  async post(input: postVoucherDto) {
    this.pending();
    const path = VoucherGateway.path;
    const dto: postVoucherDto = { ...input, schemaList: input.schemaList || this.data.schemaList };
    return this.$request.post<postVoucherDto, PostMutiRes<Voucher>>(path, dto);
  }

  async put() {
    this.pending();
    const dto = { schema: this.data.schema };
    return this.$request.put<putVoucherDto, PutRes<Voucher>>(VoucherGateway.path, dto);
  }

  async disable(dto: disableVoucherDto) {
    this.pending();
    return this.$request.delete<disableVoucherDto, DisableUUid32Res>(VoucherGateway.path, dto);
  }

  async patchVoucherAppBook(dto: patchVoucherAppBookDto) {
    this.pending();
    const path = `${VoucherGateway.path}/app/book`;
    return this.$request.patch<patchVoucherAppBookDto, null>(path, dto);
  }

  // media

  putVoucherMedia(dto: putVoucherMediaDto) {
    return this.$request.put<putVoucherMediaDto, PostRes<RefVoucherMedia>>(RefVoucherMediaGateway.path, dto);
  }

  deleteVoucherMedia(dto: deleteVoucherMediaDto) {
    return this.$request.delete<deleteVoucherMediaDto, DisableRes>(RefVoucherMediaGateway.path, dto);
  }
}
